/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.mzk.kramerius.articles.importFromLn.krameriusAccess;

import cz.mzk.kramerius.commons.DatastreamId;
import cz.mzk.kramerius.commons.Dom4jUtils;
import cz.mzk.kramerius.fedoraaccess.FedoraConnector;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.dom4j.Document;
import org.dom4j.Node;
import org.dom4j.XPath;

/**
 * This class stores maping of volume number to the PID of volume digital object for given periodical
 * @author Martin Řehánek (rehan@mzk.cz)
 */
public class PeriodicalAccessImpl implements PeriodicalAccess {

    private static String VOLUME_PID_PREFIX = "info:fedora/";
    private String periodicalPid;
    private Map<Integer, String> volumeNumberPidMap;//volumeNumber:volumePid
    private Map<Integer, Integer> volumeNumberYearMap;//volumeNumber:volumeYear
    private FedoraConnector connector;
    
    public PeriodicalAccessImpl(String periodcialPid, FedoraConnector connector) {
        this.connector = connector;
        this.periodicalPid = periodcialPid;
        volumeNumberPidMap = new HashMap<Integer, String>();
        volumeNumberYearMap = new HashMap<Integer, Integer>();
        initVolumeNumberPidMap();
    }

    private void initVolumeNumberPidMap() {
        List<String> volumePidList = getVolumes();
        for (String volPid : volumePidList) {
            System.gc();
            Document modsDoc = getModsDoc(volPid);
            Integer volNumber = getVolumeNumber(modsDoc, volPid);
            Integer volYear = getVolumeYear(modsDoc, volPid);
            if (volNumber != null && volYear != null) {
                addVolumeNumberPidMapping(volNumber, volPid);
                addVolumeNumberYearMapping(volNumber, volYear);
            } else {
                if (volNumber == null) {
                    Logger.getLogger(PeriodicalAccessImpl.class.getName()).log(Level.SEVERE, "No volume number in BIBLIO_MODS stream for volume with pid ''{0}''", volPid);
                }
                if (volYear == null) {
                    Logger.getLogger(PeriodicalAccessImpl.class.getName()).log(Level.SEVERE, "No volume year in BIBLIO_MODS stream for volume with pid ''{0}''", volPid);
                }
            }

        }
    }

    /**
     * @return List of pids of volumes
     */
    private List<String> getVolumes() {
        Document relsExtDoc = connector.getXmlDatastreamDissemination(periodicalPid, DatastreamId.RELS_EXT);
        XPath xpath = Dom4jUtils.createXPath("//kramerius:hasVolume/@rdf:resource");
        List<Node> attributeNodes = xpath.selectNodes(relsExtDoc);
        //List<Node> attributeNodes = relsExtDoc.selectNodes("//kramerius:hasVolume/@rdf:resource");
        List<String> pidList = new ArrayList<String>();
        for (Node node : attributeNodes) {
            String prefixedPid = node.getText();
            //System.out.println("prefixed: " + prefixedPid);
            String pid = prefixedPid.substring(VOLUME_PID_PREFIX.length());
            //System.out.println("pid: " + pid);
            pidList.add(pid);
        }
        return pidList;
    }

    private Document getModsDoc(String volumePid) {
        return connector.getXmlDatastreamDissemination(volumePid, DatastreamId.BIBLIO_MODS);
    }

    private Integer getVolumeNumber(Document modsDoc, String volumePid) {
        XPath xpath = Dom4jUtils.createXPath("//mods:part/mods:detail[@type='volume']/mods:number");
        String volumeNumStr = xpath.selectSingleNode(modsDoc).getText();
        if (volumeNumStr == null) {
            Logger.getLogger(PeriodicalAccessImpl.class.getName()).log(Level.SEVERE, "Cannot find content of element mods:number for volume with pid ''{0}''", volumePid);
            return null;
        }
        Integer volumeNum = null;
        try {
            volumeNum = Integer.valueOf(volumeNumStr);
        } catch (NumberFormatException e) {
            String[] params = {volumeNumStr, volumePid};
            Logger.getLogger(PeriodicalAccessImpl.class.getName()).log(Level.SEVERE, "Illegal value in element mods:number ''{0}'' for volume with pid ''{1}''", params);
            return null;
        }
        return volumeNum;
    }

    private Integer getVolumeYear(Document modsDoc, String volumePid) {
        XPath xpath = Dom4jUtils.createXPath("//mods:part/mods:date");
        String volumeYearStr = xpath.selectSingleNode(modsDoc).getText();
        if (volumeYearStr == null) {
            Logger.getLogger(PeriodicalAccessImpl.class.getName()).log(Level.SEVERE, "Cannot find content of element mods:date for volume with pid ''{0}''", volumePid);
            return null;
        }
        Integer volumeYear = null;
        try {
            volumeYear = Integer.valueOf(volumeYearStr);
        } catch (NumberFormatException e) {
            String[] params = {volumeYearStr, volumePid};
            Logger.getLogger(PeriodicalAccessImpl.class.getName()).log(Level.SEVERE, "Illegal value in element mods:date ''{0}'' for volume with pid ''{1}''", params);
            return null;
        }
        return volumeYear;
    }

    /**
     *
     * @param volumeNumber
     * @param volumePid
     */
    private void addVolumeNumberPidMapping(Integer volumeNumber, String volumePid) {
        String oldPid = volumeNumberPidMap.get(volumeNumber);
        if (oldPid != null) {
            Logger.getLogger(PeriodicalAccessImpl.class.getName()).log(Level.SEVERE, "pid for volume {0} already present. Found pid: ''{1}'', wanted to add pid: ''{2}'' ", new Object[]{volumeNumber, oldPid, volumePid});
        } else {
            volumeNumberPidMap.put(volumeNumber, volumePid);
        }
    }

    private void addVolumeNumberYearMapping(Integer volumeNumber, Integer volumeYear) {
        Integer oldYear = volumeNumberYearMap.get(volumeNumber);
        if (oldYear != null) {
            Logger.getLogger(PeriodicalAccessImpl.class.getName()).log(Level.SEVERE, "year for volume {0} already present. Found year: ''{1}'', wanted to add year: ''{2}'' ", new Object[]{volumeNumber, oldYear, volumeYear});
        } else {
            volumeNumberYearMap.put(volumeNumber, volumeYear);
        }
    }

    @Override
    public String getPeriodicalPid() {
        return periodicalPid;
    }

    /**
     *
     * @param volumeNumber
     * @return PID of volume or null if volume is not present
     */
    @Override
    public String getVolumePid(Integer volumeNumber) {
        return volumeNumberPidMap.get(volumeNumber);
    }

    /**
     *
     * @param volumeNumber
     * @return year of volume or null if volume is not present
     */
    @Override
    public Integer getVolumeYear(int volumeNumber) {
        return volumeNumberYearMap.get(Integer.valueOf(volumeNumber));
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final PeriodicalAccessImpl other = (PeriodicalAccessImpl) obj;
        if ((this.periodicalPid == null) ? (other.periodicalPid != null) : !this.periodicalPid.equals(other.periodicalPid)) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 19 * hash + (this.periodicalPid != null ? this.periodicalPid.hashCode() : 0);
        return hash;
    }
    }
